# 保险花销预测进阶

# 特征工程： 导入数据后先分析数据的相关性，剔除掉相关性不大的X项，或者处理成发散度小的值
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error

data_csv = pd.read_csv("data/insurance_data.csv")

# 分析性别与花销相关性(根据图形发现，性别不同不影响花销，所以可以剔除)
sns.kdeplot(data_csv.loc[data_csv.sex == 'male', 'charges'], fill=True, label='male')
sns.kdeplot(data_csv.loc[data_csv.sex == 'female', 'charges'], fill=True, label='female')
# plt.show()
data_csv = data_csv.drop('sex', axis=1)

# 分析region与花销相关性(可以看出region与花销很低相关性)
print(data_csv.loc[:, 'region'].unique())
sns.kdeplot(data_csv.loc[data_csv.region == 'northeast', 'charges'], fill=True, label='northeast')
sns.kdeplot(data_csv.loc[data_csv.region == 'southwest', 'charges'], fill=True, label='southwest')
sns.kdeplot(data_csv.loc[data_csv.region == 'northwest', 'charges'], fill=True, label='northwest')
sns.kdeplot(data_csv.loc[data_csv.region == 'southeast', 'charges'], fill=True, label='southeast')
# plt.show()
data_csv = data_csv.drop('region', axis=1)
# 分析smoker与花销相关性的影响(有相关性)
sns.kdeplot(data_csv.loc[data_csv.smoker == 'yes', 'charges'], fill=True, label='smoker')
sns.kdeplot(data_csv.loc[data_csv.smoker == 'no', 'charges'], fill=True, label='no')
# plt.show()
# 分析小孩数与花销相关性的影响(相关性比较大)
print(data_csv.loc[:, 'children'].unique())
sns.kdeplot(data_csv.loc[data_csv.children == 0, 'charges'], fill=True, label='children0', color='blue')
sns.kdeplot(data_csv.loc[data_csv.children == 1, 'charges'], fill=True, label='children1')
sns.kdeplot(data_csv.loc[data_csv.children == 2, 'charges'], fill=True, label='children2')
sns.kdeplot(data_csv.loc[data_csv.children == 3, 'charges'], fill=True, label='children3')
sns.kdeplot(data_csv.loc[data_csv.children == 4, 'charges'], fill=True, label='children4')


# plt.show()
# 用于将样本中每个 bmi 特征大于等于 30 的变成标签‘over’，小于30 的变成‘under’，这样做是建立在知道医学上 bmi 这个肥胖指数 30 是个很关键的阈值，大于等于30 属于肥胖，小于 30 不属于肥胖
def greater(data, flag1):
    data['bmi'] = 'over' if data['bmi'] >= flag1 else 'under'
    return data


data = data_csv.apply(greater, axis=1, args=(30,))
data = pd.get_dummies(data)
print(data.head())

X = data.drop('charges', axis=1)
Y = data['charges']
# 区分测试集和训练集
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.3)
# 归一化
scaler = StandardScaler(with_mean=True, with_std=True).fit(X_train)
X_train_scaler = scaler.fit_transform(X_train)
X_test_scaler = scaler.transform(X_test)
# 多项式升维
features = PolynomialFeatures(degree=2, include_bias=False)
X_train_scaler = features.fit_transform(X_train_scaler)
X_test_scaler = features.transform(X_test_scaler)
# 是有L2正则模型处理
rge = Ridge(alpha=10,fit_intercept=True)
rge.fit(X_train_scaler, np.log1p(Y_train))
print("系数:", rge.coef_)
print("截距:", rge.intercept_)
# 7.评估
y_pred_train = rge.predict(X_train_scaler)
y_pred_test = rge.predict(X_test_scaler)
train_mse = mean_squared_error(y_true=np.log1p(Y_train), y_pred=y_pred_train)
test_mse = mean_squared_error(y_true=np.log1p(Y_test), y_pred=y_pred_test)
print('训练集评估log1p：', train_mse)
print('测试集评估log1p：', test_mse)
train_mse = np.sqrt(mean_squared_error(y_true=np.log1p(Y_train), y_pred=y_pred_train))
test_mse = np.sqrt(mean_squared_error(y_true=np.log1p(Y_test), y_pred=y_pred_test))
print('训练集评估sqrt(log1p)：', train_mse)
print('测试集评估sqrt(log1p)：', test_mse)
train_mse = mean_squared_error(y_true=Y_train, y_pred=y_pred_train)
test_mse = mean_squared_error(y_true=Y_test, y_pred=y_pred_test)
print('训练集评估：', train_mse)
print('测试集评估：', test_mse)